<template>
  <div class="cropperImg">
    <!-- 遮罩层 -->
    <div class="container" v-show="panel">
      <div>
        <img id="image" :src="url" alt="Picture" />
      </div>
      <button type="button" id="cancelbutton" @click="panel = false">取消</button>
      <button type="button" id="button" @click="crop">确定</button>
    </div>

    <div>
      <div class="show">
        <img class="picture" :src="cropperImg.indexOf('http') === -1 ? view + cropperImg : cropperImg" alt :onerror="defaultAvatarImg" />
        <div class="input-file">
          <input type="file" id="change" :disabled="disabled" class="img_input_cropper" accept="image" @change="change($event)" />
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import Cropper from 'cropperjs' // 引入插件
import defaultAvatar from '@/assets/images/base/ic_default_portrait.png'
import axios from 'axios'
import Qs from 'qs'
export default {
  model: {
    prop: 'headerImage',
    event: 'change'
  },
  props: {
    headerImage: { // 从父组件传过来数据库图片，即用户从本地上传的图片
      type: String,
      default: ''
    },
    // 切片大小
    chunkSize: {
      type: Number,
      default: 10 * 1024 * 1024
    },
    // 切片大小
    disabled: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      cropperImg: '',
      view: '/API-DOCUMENT/document/file/download?id=',
      defaultAvatarImg: 'this.src="' + defaultAvatar + '"',
      picValue: '',
      cropper: '',
      croppable: false,
      panel: false,
      url: '',
      postHeaderImg: '',
      uploadFiles: [],
      fileIndex: 0,
      worker: null
    }
  },
  mounted() {
    // 初始化这个裁剪框
    var self = this
    var image = document.getElementById('image')
    this.cropper = new Cropper(image, {
      aspectRatio: 1,
      viewMode: 1,
      background: false,
      zoomable: false,
      ready: () => {
        self.croppable = true
      }
    })
  },
  methods: {
    changeHead() {
      document.querySelector('.img_input_cropper').click()
    },
    getObjectURL(file) {
      var url = null
      if (window.createObjectURL != undefined) { // basic
        url = window.createObjectURL(file)
      } else if (window.URL != undefined) { // mozilla(firefox)
        url = window.URL.createObjectURL(file)
      } else if (window.webkitURL != undefined) { // webkit or chrome
        url = window.webkitURL.createObjectURL(file)
      }
      return url
    },
    change(e) {
      const files = e.target.files || e.dataTransfer.files
      if (!files.length) return
      this.panel = true
      this.picValue = files[0]
      this.url = this.getObjectURL(this.picValue)
      // 每次替换图片要重新得到新的url
      if (this.cropper) {
        this.cropper.replace(this.url)
      }
      this.panel = true
    },
    async crop() {
      this.panel = false
      var croppedCanvas
      var roundedCanvas

      if (!this.croppable) {
        return
      }
      // Crop
      croppedCanvas = this.cropper.getCroppedCanvas()
      // Round
      roundedCanvas = this.getRoundedCanvas(croppedCanvas)

      this.postHeaderImg = roundedCanvas.toDataURL()
      const tfile = this.dataURLtoFile(this.postHeaderImg, this.picValue.name)
      const chunknum = Math.ceil(tfile.size / this.chunkSize)
      const name = tfile.name || ''
      const size = tfile.size
      const md5 = await this.calculateHash(this.createFileChunk(tfile))
      const obj = {
        md5: md5,
        fileName: name,
        fileSize: size,
        chunkNum: chunknum,
        chunkSize: this.chunkSize
      }

      await this.getUUID(obj).then((data) => {
        const chunks = this.createFileChunk(tfile)
        tfile['UUID'] = data
        tfile['chunks'] = chunks.map(({ file }, index) => ({
          UUID: data,
          fileName: this.picValue.name,
          index,
          chunk: file,
          size: file.size
        }))
        this.uploadChunks(tfile)
      })
    },
    /**
       * 提交文件获取UUID
       */
    async getUUID(params) {
      return new Promise((resolve, reject) => {
        axios.post('/API-DOCUMENT/document/file/insert', params, {
          transformRequest: [(data) => {
            data = Qs.stringify(data)
            return data
          }]
        }).then((res) => {
          if (res.data.code === 200 || res.data.code === '200') {
            resolve(res.data.data)
          } else {
            this.$message.warning(res.data.msg || '文件读取失败')
            resolve()
          }
        }).catch((err) => {
          reject(err)
        })
      })
    },

    /**
       * 创建文件切片
       */
    createFileChunk(file, size = this.chunkSize) {
      const list = []
      var count = 0
      while (count < file.size) {
        list.push({
          file: file.slice(count, count + size)
        })
        count += size
      }
      return list
    },

    /**
       * 生成文件 hash（web-worker）
       */
    calculateHash(chunks) {
      return new Promise((resolve) => {
        this.worker = new Worker('./hash.js')
        this.worker.postMessage({ chunks })
        this.worker.onmessage = (e) => {
          const { hash } = e.data
          if (hash) {
            resolve(hash)
          }
        }
      })
    },

    /**
       * 将切片传输给服务端
       */
    async uploadChunks(file) {
      const _this = this
      var chunkData = file.chunks
      return new Promise(async (resolve, reject) => {
        const requestDataList = chunkData.map(({ chunk, UUID, fileName, index }) => {
          const formData = new FormData()
          formData.append('UUID', UUID)
          formData.append('file', chunk)
          formData.append('fileIndex', index) // 文件名使用切片的下标
          return { formData, index, fileName, UUID }
        })

        try {
          await this.sendRequest(requestDataList, chunkData)
        } catch (error) {
          return
        }

        try {
          await this.mergeRequest(file)
          resolve()
        } catch (error) {
          reject()
        }
      })
    },

    /**
       * 上传切片
       */
    sendRequest(forms, chunkData) {
      const that = this
      const {
        lang,
        field,
        ki
      } = this
      return new Promise((resolve, reject) => {
        const formInfo = forms.shift()
        const formData = formInfo.formData
        axios.post('/API-DOCUMENT/document/file/chunk', formData, {
          onUploadProgress: event => {
            if (event.lengthComputable) {
              that.progress = 100 * Math.round(event.loaded) / event.total
            }
          },
          headers: {
            'Content-Type': 'multipart/form-data'
          }
        }).then((res) => {
          if (res.data.code === 200 || res.data.code === '200') {
            resolve()
          } else {
            _this.hasError = true
            _this.errorMsg = lang.fail
            _this.$emit('crop-upload-fail', resData.data.msg, field, ki)
          }
        }).catch((e) => {
          if (that.value) {
            that.loading = 3
            that.hasError = true
            that.errorMsg = lang.fail
            that.$emit('crop-upload-fail', e, field, ki)
          }
        })
      })
    },

    /**
       * 通知服务端合并切片
       */
    mergeRequest(file) {
      const _this = this
      return new Promise((resolve, reject) => {
        axios.post('/API-DOCUMENT/document/file/merge', { UUID: file.UUID }, {
          transformRequest: [(data) => {
            data = Qs.stringify(data)
            return data
          }]
        }).then((res) => {
          if (res.data.code === 200 || res.data.code === '200') {
            _this.$emit('change', res.data.data.id)
            _this.cropperImg = res.data.data.id
            _this.$emit('changeSuccess', res.data.data.id)
            resolve()
          } else {
            _this.$dialog.toast({
              mes: resData.data.msg || '服务器繁忙请稍后再试。',
              timeout: 1000,
              icon: 'error'
            })
            resolve()
          }
        }).catch((e) => {
          _this.$dialog.toast({
            mes: '服务器繁忙请稍后再试。',
            timeout: 1000,
            icon: 'error'
          })
          reject()
        })
      })
    },
    dataURLtoFile: function (dataurl, filename) {
      var arr = dataurl.split(',')
      var mime = arr[0].match(/:(.*?);/)[1]
      var bstr = atob(arr[1])
      var n = bstr.length
      var u8arr = new Uint8Array(n)
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n)
      }
      return new File([u8arr], filename, { type: mime })
    },
    getRoundedCanvas(sourceCanvas) {
      var canvas = document.createElement('canvas')
      var context = canvas.getContext('2d')
      var width = sourceCanvas.width
      var height = sourceCanvas.height
      // 为了每张图片的大小不超过50k,所以不论裁剪多大，最后上传的，最大宽度不超过90。
      if (width > 90) {
        width = 90
      }
      if (height > 90) {
        height = 90
      }

      canvas.width = width
      canvas.height = height

      context.imageSmoothingEnabled = true
      context.drawImage(sourceCanvas, 0, 0, width, height)
      context.globalCompositeOperation = 'destination-in'
      context.beginPath()
      context.arc(width / 2, height / 2, Math.min(width, height) / 2, 0, 2 * Math.PI, true)
      context.fill()

      return canvas
    }
  },
  created() {
    this.cropperImg = this.headerImage
  },
  watch: {
    headerImage: function (val) {
      this.cropperImg = val
    }
  }
}
</script>
<style lang="scss">
.cropperImg {
  .show {
    width: rem(102);
    height: rem(102);
    margin-right: rem(8);
    overflow: hidden;
    position: relative;
    border-radius: 50%;
    border: 1px solid #e5e5e5;
    .input-file {
      width: 100%;
      height: 100%;
      position: absolute;
      opacity: 0;
      top: 50%;
      transform: translateY(-50%);
      input {
        width: 100%;
        height: 100%;
      }
    }
  }
  .picture {
    width: 100%;
    height: 100%;
    overflow: hidden;
    background-position: center center;
    background-repeat: no-repeat;
    background-size: cover;
  }
  .container {
    z-index: 9999999;
    position: fixed;
    margin: 0;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 1);
    display: flex;
    align-items: center;
    justify-content: center;
  }
  #image {
    max-width: 100%;
  }
}

.cropperImg #button {
  position: absolute;
  right: rem(10);
  top: rem(10);
  width: rem(160);
  height: rem(80);
  border: none;
  border-radius: rem(5);
  background: transparent;
  color: rgba(255, 255, 255, 0.9);
  font-size: rem(32);
}
.cropperImg #cancelbutton {
  position: absolute;
  left: rem(10);
  top: rem(10);
  width: rem(160);
  height: rem(80);
  border: none;
  border-radius: rem(5);
  background: transparent;
  color: rgba(255, 255, 255, 0.9);
  font-size: rem(32);
}

.cropper-view-box,
.cropper-face {
  border-radius: 50%;
}

/*!
   * Cropper.js v1.0.0-rc
   * https://github.com/fengyuanchen/cropperjs
   *
   * Copyright (c) 2017 Fengyuan Chen
   * Released under the MIT license
   *
   * Date: 2017-03-25T12:02:21.062Z
   */

.cropper-container {
  font-size: 0;
  line-height: 0;
  position: relative;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  direction: ltr;
  -ms-touch-action: none;
  touch-action: none;
}

.cropper-container img {
  /* Avoid margin top issue (Occur only when margin-top <= -height) */
  display: block;
  min-width: 0 !important;
  max-width: none !important;
  min-height: 0 !important;
  max-height: none !important;
  width: 100%;
  height: 100%;
  image-orientation: 0deg;
}

.cropper-wrap-box,
.cropper-canvas,
.cropper-drag-box,
.cropper-crop-box,
.cropper-modal {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}

.cropper-wrap-box {
  overflow: hidden;
}

.cropper-drag-box {
  opacity: 0;
  background-color: #fff;
}

.cropper-modal {
  opacity: 0.5;
  background-color: #000;
}

.cropper-view-box {
  display: block;
  overflow: hidden;

  width: 100%;
  height: 100%;

  outline: 1px solid #39f;
  outline-color: rgba(51, 153, 255, 0.75);
}

.cropper-dashed {
  position: absolute;

  display: block;

  opacity: 0.5;
  border: 0 dashed #eee;
}

.cropper-dashed.dashed-h {
  top: 33.33333%;
  left: 0;
  width: 100%;
  height: 33.33333%;
  border-top-width: 1px;
  border-bottom-width: 1px;
}

.cropper-dashed.dashed-v {
  top: 0;
  left: 33.33333%;
  width: 33.33333%;
  height: 100%;
  border-right-width: 1px;
  border-left-width: 1px;
}

.cropper-center {
  position: absolute;
  top: 50%;
  left: 50%;

  display: block;

  width: 0;
  height: 0;

  opacity: 0.75;
}

.cropper-center:before,
.cropper-center:after {
  position: absolute;
  display: block;
  content: ' ';
  background-color: #eee;
}

.cropper-center:before {
  top: 0;
  left: -3px;
  width: 7px;
  height: 1px;
}

.cropper-center:after {
  top: -3px;
  left: 0;
  width: 1px;
  height: 7px;
}

.cropper-face,
.cropper-line,
.cropper-point {
  position: absolute;

  display: block;

  width: 100%;
  height: 100%;

  opacity: 0.1;
}

.cropper-face {
  top: 0;
  left: 0;

  background-color: #fff;
}

.cropper-line {
  background-color: #39f;
}

.cropper-line.line-e {
  top: 0;
  right: -3px;
  width: 5px;
  cursor: e-resize;
}

.cropper-line.line-n {
  top: -3px;
  left: 0;
  height: 5px;
  cursor: n-resize;
}

.cropper-line.line-w {
  top: 0;
  left: -3px;
  width: 5px;
  cursor: w-resize;
}

.cropper-line.line-s {
  bottom: -3px;
  left: 0;
  height: 5px;
  cursor: s-resize;
}

.cropper-point {
  width: 5px;
  height: 5px;

  opacity: 0.75;
  background-color: #39f;
}

.cropper-point.point-e {
  top: 50%;
  right: -3px;
  margin-top: -3px;
  cursor: e-resize;
}

.cropper-point.point-n {
  top: -3px;
  left: 50%;
  margin-left: -3px;
  cursor: n-resize;
}

.cropper-point.point-w {
  top: 50%;
  left: -3px;
  margin-top: -3px;
  cursor: w-resize;
}

.cropper-point.point-s {
  bottom: -3px;
  left: 50%;
  margin-left: -3px;
  cursor: s-resize;
}

.cropper-point.point-ne {
  top: -3px;
  right: -3px;
  cursor: ne-resize;
}

.cropper-point.point-nw {
  top: -3px;
  left: -3px;
  cursor: nw-resize;
}

.cropper-point.point-sw {
  bottom: -3px;
  left: -3px;
  cursor: sw-resize;
}

.cropper-point.point-se {
  right: -3px;
  bottom: -3px;
  width: 20px;
  height: 20px;
  cursor: se-resize;
  opacity: 1;
}

@media (min-width: 768px) {
  .cropper-point.point-se {
    width: 15px;
    height: 15px;
  }
}

@media (min-width: 992px) {
  .cropper-point.point-se {
    width: 10px;
    height: 10px;
  }
}

@media (min-width: 1200px) {
  .cropper-point.point-se {
    width: 5px;
    height: 5px;
    opacity: 0.75;
  }
}

.cropper-point.point-se:before {
  position: absolute;
  right: -50%;
  bottom: -50%;
  display: block;
  width: 200%;
  height: 200%;
  content: ' ';
  opacity: 0;
  background-color: #39f;
}

.cropper-invisible {
  opacity: 0;
}

.cropper-bg {
  background-image: url('');
}

.cropper-hide {
  position: absolute;

  display: block;

  width: 0;
  height: 0;
}

.cropper-hidden {
  display: none !important;
}

.cropper-move {
  cursor: move;
}

.cropper-crop {
  cursor: crosshair;
}

.cropper-disabled .cropper-drag-box,
.cropper-disabled .cropper-face,
.cropper-disabled .cropper-line,
.cropper-disabled .cropper-point {
  cursor: not-allowed;
}
</style>
